/**
 * 
 * Main skeleton
 * @author manel
 */

if (typeof console == "undefined") {
	console = {log:function() {}}; // sets empty function to console log if doesn't exist
}	


if (typeof opera != "undefined") {
	console.log = opera.postError;	
}


/*
 * Game global data
 */
var GAME = {};

var match;


var preloadImages = (function () {
	var list = {};
	
	return {
		add:function (image, src, fn){
			
			if (typeof list.image != "undefined") {
				console.log("Duplicated image "+image+" "+src);
			}
			
			list[image] = new Image();
			list[image].src = src;
			list[image].completed = false;
			list[image].onload = function() {
				list[image].completed = true;
				if (typeof fn == "function") {
					fn(list[image], image);
				}
			}
		},
		getList: function() {
			return list;
		},
		getImage: function(image) {
			return list[image];
		},
		checkComplete: function() {
			
			for(var k in list) {
				if (!list[k].completed) 
					return false; 
			}
			
			return true; 
		}
	} 
	 
})();

var loadingMask = function(values) {

	var baseStr = "Loading ";
	var loadingStr = "";
	var loadValues = {};
	
	function buildValues() {
		loadingStr = "<br />";
		
		for (var k=0; k<values.length; k++) {
			if (typeof loadValues[values[k]] == "undefined") {
				loadValues[values[k]] = '';
			}
			loadingStr += baseStr + values[k] + "..."+ loadValues[values[k]] +"<br />";
			
		}
		loadingStr += "<br /><img src='static/images/ajax-loader.gif' />";
	}
	
	buildValues();
	
	return {
		update:function (val) {
			loadValues[val] = "OK";
			buildValues();
			this.block();	
		},
		block: function() {
			$.blockUI({message:loadingStr});
		},
		unblock: function() {
			for(var k in loadValues) {
				loadValues[k] = "";
			}
			
			$.unblockUI();
		}
	}
}


// action starts here
$(document).ready(function() {
	
	// check for IE
	if ($.browser.msie) {
		$.blockUI({message:_("Dreadful Fire doen't work on Internet Explorer currently")});
		return;
	}
	

	$.blockUI({message:"Starting..."});
	
	var imageLoadedCallback = function(el,image) {
		
		if (preloadImages.checkComplete()) {
			$.unblockUI();
		}
	}
	
	// image preloading
	preloadImages.add("hero", "happy square final 40x40.png", imageLoadedCallback);	
	preloadImages.add("enemy1", "face-angry.png", imageLoadedCallback);
	preloadImages.add("enemy2", "face-angry.png", imageLoadedCallback);

	preloadImages.add("clickHere", "static/images/click here.png", imageLoadedCallback);

	
	tbLauncher = $("#thickboxLauncher");
	
	
	$("#tagsInput").blur(function(e) {
		if (typeof e.target.value == "string" && e.target.value.length > 2) {
            searchTags(e.target.value);
        }
	}).bind("keypress", function(e) {
		if (e.which == 13) {
			this.blur();
		}
		
	}).focus(); // sets start focus
	
	var canvas = $("#screen");
	
	// check for canvas support
	if (canvas[0].getContext){
	  var ctx = canvas[0].getContext('2d');
	} else {
		alert("Canvas is not supported");
	}

	GAME.photoChooser = new Chooser(); 
	
	var photoLoadMask = new loadingMask(["info","photo"]);
	
	var bgImage = new Image();
	bgImage.id = "bgImage";
	
	function searchTags(str){
		
		photoLoadMask.block();
		
		flickrQuery.getData(str, function(data){
			if (data.total < 1) {
				alert("Tags don't match any photograph");
				return;
			}
			
			if (data.total < 10) {
                $.unblockUI();
				alert("Do not found enough tags.");
                $("#tagsInput").val("").focus();
				return;
			}
			
			GAME.photos = data.photos;
			
			photoLoadMask.update("info");			
			
			// display matches
			$("#foundPhotos").html("Found " + data.total + " photos.");
			
			// choose one randomly
			GAME.photoChooser.setList(data.photos);
			
			// load first image
			var n = GAME.photoChooser.getRandomValue();
			var photo = GAME.photos[n];
			
			flickrQuery.getSizes(photo.id, function(data){
				var tmpImage = flickrQuery.getBestSize(data, CONFIG.screenWidth, CONFIG.screenHeight);
				if (!tmpImage) {
					console.log("Photo sizes don't fit");
				} else {
					bgImage.src = tmpImage.source;
					$("#imgWindow").attr("src", tmpImage.source);
					bgImage.width = tmpImage.width;
					bgImage.height = tmpImage.height;
					
					bgImage.onload = function(){
						startGame(bgImage, data);
					}
				}
				
			});
		});
	}
	
	
	/**
	 * Game launcher
	 * @param {Object} bgImage
	 */
	function startGame(bgImage) {
	 	  photoLoadMask.unblock();
			
		  nextLevel(ctx, bgImage);
	}
	
});


function nextLevel(ctx,bgImage) {

	  match = Game(ctx,bgImage);
	  gameLoop = match.gameLoop;
	  match.gameLoop();
	
}

function fps() {
	//console.log(match.fpsCounter()); 
} 
 

/**
 * Game class
 * @param {Object} ctx canvas context
 * @param {Object} bgImage
 */
var Game = function(ctx,bgImage){
	
	var that = this;
	
	var globalTimer = Timer();
	var fpsCounter = 0;

	var stage = 1;
	$("#stage").html("Stage "+stage);


	var percent = $("#percent");
	
	var followingImage = new Image();
	
	var i = preloadImages.getImage("clickHere");
	ctx.drawImage(i, 0, 0);
	
	// restart next level
	var nextLevel = function() {
		
	}
	
	
	var preloadFollowingImage = function() {
		
		var n = GAME.photoChooser.getRandomValue();
		var photo = GAME.photos[n];
		
		flickrQuery.getSizes(photo.id, function(data){
			var tmpImage = flickrQuery.getBestSize(data, CONFIG.screenWidth, CONFIG.screenHeight);
			if (!tmpImage) {
				console.log("Photo sizes don't fit");
			} else {
				followingImage.src = tmpImage.source;
				followingImage.width = tmpImage.width;
				followingImage.height = tmpImage.height;
			}
			
		});
	}
	
	preloadFollowingImage();

	
	// lives
	var livesManager = function () {
		var live = [($("#live1"))[0], ($("#live2"))[0], ($("#live3"))[0]];
		var nOfLives;
		var maxLives = CONFIG.maxLives;
		
		return {
			init: function() {
				nOfLives = maxLives;
			}, 
			draw: function() {
				
			},
			add: function() {
				if (nOfLives < maxLives) {
					nOfLives++;
				}
			},
			minus: function() {
				if (nOfLives > 0) {
					nOfLives--;
				}
				
			}
		}
		
	}();


	/**
	 * Sets the score
	 */
	scoreBar = (function(){
		
		var redBar = $("#redBar");
		var greenBar = $("#greenBar");
		
		var lives = [ $("#live1"), $("#live2"), $("#live3") ];
		
		return {
			setHealth: function(n) {
				n = clip(n, 0, CONFIG.maxHealth);
				var redWidth = CONFIG.maxHealth - n;
				
				redBar.width(redWidth);
				greenBar.width(n);

			},
			setLives: function(n) {
				for (var k = 0; k<CONFIG.maxLives; k++) {
					//console.log(lives[k].attr("visibility"),"*");
					if (k < n) {
						lives[k].css("visibility","visible");
					} else {
						lives[k].css("visibility","hidden");
					}
				}
				
			}
		}
	})();	
	
	// initialize mask
	
	var mask = function(){

		this.limit = new Array(CONFIG.screenHeight);
		
		
		this.reset = function() {
			for (var k = 0; k < CONFIG.screenHeight; k++) {
				this.limit[k] = [CONFIG.screenWidth +1, -1];
			}	
		}
	
		this.check = function(x,y) {
				
		};
		
		this.setLine = function(line,min,max) {
			this.limit[line] = [min,max];
		};
		
		this.move = function(minY, maxY, minX, maxX) {
			for (var k2 = minY; k2 < maxY; k2++) {
				if (this.limit[k2][0] > minX) {
					this.limit[k2][0] = minX; 
				} 
				
				if (this.limit[k2][1] < maxX) {
					this.limit[k2][1] = maxX; 
				}
				
			}
			
		};
		
		this.draw = function() {

			ctx.beginPath();
			
			for (var k2=0; k2<this.limit.length; k2+=2) {
				if (this.limit[k2][1] == -1) continue; // do not clip unneeded lines 
				 
				var startX = this.limit[k2][0];
				var endX = this.limit[k2][1];
				ctx.moveTo(startX,k2);
				ctx.lineTo(endX,k2);
				ctx.lineTo(endX,k2+2);
				ctx.lineTo(startX,k2+2);
			}
			ctx.clip();
			
		};
		
		this.getCleanedArea = function () {
			var acum = 0, tmp = 0;

			for (var k = 0; k < this.limit.length; k++) {
				tmp = this.limit[k][1] - this.limit[k][0];
				if (tmp > 0) {
					acum += tmp;
				}
			}
			
			return acum;
		};
		
		this.reset();
	};

	
	var playerMask = new mask();
	
	var fpsHandler = window.setInterval(fps,1000);
	
	/**
	 * Module that controls the hero
	 * @param {int} x start coordinates
	 * @param {int} y 
	 */
	function Player(x,y) {
	
		var that = this;
		
		this.health = CONFIG.maxHealth;
		this.lives = CONFIG.maxLives;
	
	    var w = CONFIG.heroWidth, h = CONFIG.heroHeight;
		var w2 = w/2, h2 = h/2;
		var ew2 = CONFIG.enemyWidth / 2, eh2 = CONFIG.enemyHeight / 2;
		
		this.w = w; this.h = h;
	
		this.x = x | CONFIG.heroStartX;
		this.y = y | CONFIG.heroStartY;
		
		this.damage = false; // used in collision detection
		
		this.initScore = function () {
			that.score = {
				lives: CONFIG.maxLives,
				health: CONFIG.maxHealth
			}
		}
		
		this.initScore();
		
		var playerImage = preloadImages.getImage("hero");
		
		for (var k = 0; k<h; k++) {
			playerMask.setLine(this.y+k,this.x,this.x+w); 
		}
		
		this.drawMask = function() {
			playerMask.move(this.y, this.y + h, this.x, this.x + w);
			playerMask.draw();
		}
		
		this.collisionDetectionStart = function() {
			
			this.cp = [
				[this.x, this.y],
				[this.x + w2, this.y],
				[this.x + w, this.y],
				
				[this.x, this.y + h2],
				[this.x + w, this.y + h2],

				[this.x, this.y + h],
				[this.x + w2, this.y + h],
				[this.x + w, this.y + h]
			];

		}

		this.collisionDetection = function(enemyPos,enemyRadio) {
			// calculate collision points
			var tx, ty;
			
			for(var k = 0; k < this.cp.length; k++) {
				tx = enemyPos[0] + ew2 - this.cp[k][0];
				tx = tx * tx;
				ty = enemyPos[1] + eh2 - this.cp[k][1];
				ty = ty * ty;
				
				value = tx + ty - enemyRadio;
				if (value<0) {
					this.damage = true;
				} 
			}			
		}
		
		this.damagePlayer = function(n) {
			this.health -= n;
			if (this.health<=0) {
				this.lives--;
				scoreBar.setLives(this.lives);

                scoreBar.setHealth(CONFIG.maxHealth);
                this.health = CONFIG.maxHealth;

				if (this.lives < 1) {
					GameOver();			
				}
			} 
			scoreBar.setHealth(this.health);
			
		}
		
		this.drawPlayer = function() {
			ctx.drawImage(playerImage, this.x, this.y);
		}
		
	}
	 	
	p1 = new Player();
	
	/**
	 * Enemy Builder
	 */
	function EnemyBuilder(el, startPos) {
		this.radio = CONFIG.enemyRadio;
		this.image = el;
		this.pos = [startPos[0],startPos[1]];
		this.vel = [startPos[2],startPos[3]];

		this.move = function() {
			this.pos[0] += this.vel[0];
			this.pos[1] += this.vel[1];
			
			if (this.pos[0] == CONFIG.minWidth) {
				this.vel[0] *= -1;
			} else if (this.pos[0] + CONFIG.enemyWidth == CONFIG.screenWidth) {
				this.vel[0] *= -1; 
			}

			if (this.pos[1] == CONFIG.minHeight) {
				this.vel[1] *= -1;
			} else if (this.pos[1] + CONFIG.enemyHeight == CONFIG.screenHeight) {
				this.vel[1] *= -1; 
			}
			
		}
		
		this.draw = function() {
			ctx.drawImage(this.image, this.pos[0], this.pos[1]);
		}		
	}
	
	enemyList = [
		new EnemyBuilder(preloadImages.getImage("enemy1"), CONFIG.enemy[0]),
		new EnemyBuilder(preloadImages.getImage("enemy2"), CONFIG.enemy[1]),
	];
	
	
	/*
	 * Mouse Handler
	 */
	document.onmousemove = function(event){
		mouseHandler.set.call(mouseHandler, event);
	};
	

	var mouseHandler = (function() {
		var oldDx = 0, oldDy = 0;
		
		return {
			start : function () {
			}, 
			set: function(event) {
				this.dx = event.clientX - oldDx;
				this.dy = event.clientY - oldDy;
				oldDx = event.clientX;
				oldDy = event.clientY;
				
			}
			
		}
	})();
	
	
	
	var keyboardHandler = (function() {
		CONFIG.keys = {
			KEY_LEFT : 37,
			KEY_UP : 38,
			KEY_RIGHT : 39,
			KEY_DOWN : 40,
		}
		
		var list = {};
		// clean pressed keys list
		for (var k in CONFIG.keys) { 
			list[CONFIG.keys[k]] = 0;
		}
		
		var keys = CONFIG.keys;
		
		return {
			setKeyUp : function(event) {
				list[event.keyCode] = 0;
			},
			setKeyDown : function(event) {
				list[event.keyCode] = 1;
			},
			control: function () {
				return [list[keys.KEY_RIGHT] - list[keys.KEY_LEFT], list[keys.KEY_DOWN] - list[keys.KEY_UP]];
			}
		}
		
	})();
	
	document.onkeydown = keyboardHandler.setKeyDown;
	document.onkeyup = keyboardHandler.setKeyUp;
	

	/**
	 * Main control handler
	 * Do not execute each frame, do it at a fixed speed
	 */
	var controlHandler = function() {
		var cleaned, totalPercent;
		
		return function() {
			var dx, dy;
			var movement = keyboardHandler.control();
			
			// need to compare each array member
			if (movement[0] != 0 || movement[1] != 0) { // prefer keyboard
				dx = movement[0];
				dy = movement[1];
				
				mouseHandler.dx = 0; // avoid conflict with mouse movement
				mouseHandler.dy = 0;
			} else { // but use mouse if needed
				dx = norm(mouseHandler.dx | 0);
				dy = norm(mouseHandler.dy | 0);
			}
			
			
			p1.x = clip(p1.x + dx, 0, CONFIG.screenWidth - p1.w);
			p1.y = clip(p1.y + dy, 0, CONFIG.screenHeight - p1.h);
			
			if (p1.damage) {
				p1.damagePlayer(1);
				p1.damage = false;
			}
			
			cleaned = playerMask.getCleanedArea();
			totalPercent = Math.round((cleaned / CONFIG.totalArea) * 100);

			percent.html(totalPercent);
			
			
			if (totalPercent >= CONFIG.completedPercent) {
				var w = bgImage.width;
				var h = bgImage.height;
				
				// show big image
				$("#thickboxLauncher").attr("href","#TB_inline?height="+ h +"&width="+ w +"&inlineId=divWindow").click();
				// restart
				playerMask.reset();
			}		
				 
			for (var k = 0; k < enemyList.length; k++) {
				enemyList[k].move();  
			}
			
		}
		 
	};


	var controlInterval = setInterval(controlHandler(), CONFIG.controlInterval);
	
    var gameOver = false;
    
    function GameOver() {
        gameOver = true;
        var message = _("GAME OVER<br /><br/><a href=''>Press here to try again</a>");
        $.blockUI({"message":message});
    }
	
    
	return {
		gameLoop : function() {
            if (gameOver) return;
            
			ctx.save();
			ctx.fillStyle = CONFIG.bgColor;
			ctx.fillRect(0,0,CONFIG.screenWidth,CONFIG.screenHeight);	

			p1.drawMask();
			
			ctx.drawImage(bgImage,0,0);
			ctx.restore();
			
			p1.drawPlayer();
			
			p1.collisionDetectionStart();
			
			for (var k = 0; k < enemyList.length; k++) {
				enemyList[k].draw();
				p1.collisionDetection(enemyList[k].pos, enemyList[k].radio); 
			}
			
			fpsCounter++;
			
			gameTO = setTimeout(this.gameLoop,1);	
		},
	
		fpsCounter : function() {
			var val = fpsCounter;
			fpsCounter = 0;
			return val;
		},
		
	}
	
}


Timer = function() {
	var oldTime = (new Date()).getTime();
	var startTime = oldTime;
	
	return {
		current: function() {
			newTime = (new Date()).getTime();
			var dif = newTime - oldTime;
			oldTime = newTime;
		}, 
		reset: function() {
			var oldTime = (new Date()).getTime();
		},
	}
}

